package manager

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"sync"
	"time"

	"github.com/gorilla/mux"
)

// Message - 消息体
type Message struct {
	CommonName   string
	Organization string
	PubKeyData   string
}

var mutex = &sync.Mutex{}

// RAServer - 服务器
type RAServer struct {
	manager CertificateManager
}

// Run - 启动Restful服务
func (rs *RAServer) Run() error {
	mux := rs.makeMuxRouter()
	httpPort := StoreConfig.Port
	log.Println("RA服务器正在监听端口:", httpPort)
	s := &http.Server{
		Addr:           ":" + httpPort,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	if err := s.ListenAndServe(); err != nil {
		return err
	}

	return nil
}

// create handlers
func (rs *RAServer) makeMuxRouter() http.Handler {
	muxRouter := mux.NewRouter()
	muxRouter.HandleFunc("/get_public_key", rs.handleGetPublicKey).Methods("GET")
	muxRouter.HandleFunc("/apply_cert", rs.handleApplyCert).Methods("POST")
	muxRouter.HandleFunc("/check_cert", rs.handleCheckCert).Methods("POST")
	return muxRouter
}

func (rs *RAServer) handleGetPublicKey(w http.ResponseWriter, r *http.Request) {
	pemName := "pubkey.pem"
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", pemName))
	bytes := rs.manager.GetSystemPublicKey()
	io.WriteString(w, string(bytes))
}

func (rs *RAServer) handleApplyCert(w http.ResponseWriter, r *http.Request) {
	pemName := "pubkey.pem"
	w.Header().Set("Content-Type", "application/octet-stream")
	w.Header().Set("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s\"", pemName))

	var m Message
	body, _ := ioutil.ReadAll(r.Body)
	err := json.Unmarshal(body, &m)
	if err != nil {
		fmt.Println(err.Error())
		return
	}
	defer r.Body.Close()

	mutex.Lock()
	data, _ := rs.manager.GenerateClientCert(m.CommonName, m.Organization, []byte(m.PubKeyData))
	mutex.Unlock()

	io.WriteString(w, string(data))
}

func (rs *RAServer) handleCheckCert(w http.ResponseWriter, r *http.Request) {
	body, err := ioutil.ReadAll(r.Body)
	err = rs.manager.CheckSystemSignature(body)
	if err != nil {
		rs.respondWithJSON(w, r, http.StatusCreated, []byte("error"))
	}

	rs.respondWithJSON(w, r, http.StatusCreated, []byte{})
}

func (rs *RAServer) respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
	w.Header().Set("Content-Type", "application/json")
	response, err := json.Marshal(payload)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("HTTP 500: Internal Server Error"))
		return
	}
	w.WriteHeader(code)
	w.Write(response)
}
